//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "MeshConverters.h"
#include "..\\..\\Core\\CConverter.h"

struct TDMDFile
{
private:
	TModelInfo &	_sModel;
	bool			_bAnim;

public:
	TDMDFile(TModelInfo &sInfo, bool bAnim): _sModel(sInfo), _bAnim(bAnim) {}

	bool Load();
};



bool CDMDConverter::ConvertMesh(TModelInfo &sInfo)
{
	TDMDFile	s_dmd( sInfo, false );

	return s_dmd.Load();
}


bool CDMDConverter::ConvertFrameAnim(TModelInfo &sInfo)
{
	TDMDFile	s_dmd( sInfo, true );

	return s_dmd.Load();
}


bool CDMDConverter::ConvertSceletal(TModelInfo &sInfo)
{
	return false;
}


bool CDMDConverter::ConvertAutoType(TModelInfo &sInfo)
{
	return ConvertFrameAnim( sInfo );
}



bool TDMDFile::Load()
{
#pragma pack(push, 1)
	struct TDMDHeader
	{
		uint32	uFlags;
		bool	bTexCoords;
		uint32	uNumVerts;
		uint32	uNumFaces;
	};
#pragma pack(pop)


	IFilePtr	p_file;

	if ( CORE->LoadFile( _sModel.pFileName, p_file, e_file_open_flags::DEF_READ ) != S_OK )
		return false;

	TDMDHeader	s_header;
	char		a_file_type[11];
	bool		b_read		= true;
	const char *p_name		= String::GetFileName( _sModel.pFileName );


	b_read &= p_file->ReadS( a_file_type, sizeof(a_file_type) );

	if ( a_file_type[0] != 'D' || a_file_type[1] != 'M' || a_file_type[2]!= 'D' ) {
		LOG( "Wrong DMD mesh file.", );
		return false;
	}

	if ( a_file_type[4] != '3' || a_file_type[5] != '.' || a_file_type[6] != '0' ) {
		LOG( "Incorrect DMD mesh version.", );
		return false;
	}

	b_read &= p_file->ReadS( &s_header, sizeof(s_header) );

	if ( !b_read )
		return false;

	uint8	u_face_size	  = s_header.uNumVerts > 65535 ? sizeof(uint32) : sizeof(uint16);
	uint	u_vdata_size  = s_header.uNumVerts * sizeof(float) * 6 +
							(s_header.bTexCoords ? s_header.uNumVerts * sizeof(float) * 2 : 0 );
	uint	u_idata_size  = s_header.uNumFaces * u_face_size * 3;
	bool	b_frame_anim  = EnumCmp( s_header.uFlags, 0x0001 );
	uint	u_mesh_count  = 1;

	if ( b_frame_anim )
		b_read &= p_file->ReadS( &u_mesh_count, sizeof(u_mesh_count) );
	

	_sModel.sData.aMeshes.PushBack( TModelData::TModelMesh( p_name ) );
	
	TModelData::TModelMesh&	s_model = _sModel.sData.aMeshes.Back();
	s_model.sMesh.Create( TMesh::MULTI_MESH );

	TMultiMesh &	s_mesh = *s_model.sMesh.sData.pMesh;
	binary_buffer_t	a_vert_data,
					a_idx_data;

	a_idx_data.Resize(  u_idata_size, false );
	a_vert_data.Resize( u_vdata_size, false );


	for (uint i = 0; i < u_mesh_count; ++i)
	{
		TMultiMesh::TSubMesh		s_submesh;

		float	a_points[6];

		b_read &= p_file->ReadS( a_points,			sizeof(a_points) );
		b_read &= p_file->ReadS( a_vert_data.ptr(),	a_vert_data.Size() );
		b_read &= p_file->ReadS( a_idx_data.ptr(),	a_idx_data.Size() );

		if ( !b_read )
			return false;

		const uint	u_vert_offset	= (uint)s_mesh.aVertices.Size();
		const uint	u_vert_size		= s_header.uNumVerts * sizeof(float) * 3;

		s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::POSITION, 3, 0,
							u_vert_offset, gl_vertex::FLOAT, false, 0 ) );

		s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::NORMAL, 3, 0,
							u_vert_offset + u_vert_size, gl_vertex::FLOAT, false, 0 ) );

		if ( s_header.bTexCoords )
			s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::TEXCOORD_0, 2, 0,
								u_vert_offset + u_vert_size*2, gl_vertex::FLOAT, false, 0 ) );

		s_submesh.eMode			= gl_primitive::TRIANGLE;
		s_submesh.eType			= u_face_size == sizeof(uint32) ? gl_index::UINT : gl_index::USHORT;
		s_submesh.sName			= string("mesh_") << i;
		s_submesh.uIdxCount		= s_header.uNumFaces*3;
		s_submesh.uIdxOffset	= (uint)s_mesh.aIndices.Size();
		s_submesh.sBBox.SetBBox( vec3(a_points), vec3(&a_points[2]) );

		s_mesh.aSMIndices.PushBack( s_mesh.aSubMeshes.Count() );

		s_mesh.aIndices.Append(  a_idx_data );
		s_mesh.aVertices.Append( a_vert_data );
		s_mesh.aSubMeshes.PushBack( s_submesh );
	}

	s_mesh.aLODs.PushBack( 0 );
	s_mesh.aLODs.PushBack( s_mesh.aSMIndices.Count() );


	if ( _bAnim )
	{
		s_model.uAnimationIdx = (uint)_sModel.sData.aAnimations.Count();
		_sModel.sData.aAnimations.PushBack( TModelData::TMeshAnimInfo( p_name ) );

		TMeshAnimation	&	s_anim_base = _sModel.sData.aAnimations.Back().sAnim;
		s_anim_base.Create( TMeshAnimation::FRAME );

		TFrameAnimData &	s_anim = *s_anim_base.sData.pFrameAnim;

		for (uint i = 0; i < u_mesh_count; ++i)
		{
			s_anim.aTrackFrames.PushBack( i );
		}

		s_anim.aTracks.PushBack( TFrameAnimData::TTrackInfo( 0, 0, (uint)s_anim.aTrackFrames.Count() ) );
	}

	return true;
}
